#!/usr/bin/env /bin/sh

set -eu

show_help() {
  cat <<EOF
Usage: build [options] [path]

Options:
    --help      Display this help message and exit.
    --watch     Watch dependencies and rebuild on demand.
    --test      In addition to build, run tests

Examples:
    tools/build integ-tests/go           # Build Go client and CFFI library
    tools/build integ-tests/go --test    # Build and run Go integration tests
    tools/build integ-tests/go --watch   # Watch mode for Go development

Go Integration Tests:
    The Go tests require BAML_LIBRARY_PATH to be set to the compiled CFFI library.
    This is handled automatically when using 'tools/build integ-tests/go --test'.
    
    For manual testing:
    BAML_LIBRARY_PATH=\$PWD/engine/target/debug/libbaml_cffi.dylib go test -v ./integ-tests/go
EOF
}

_help_mode=0
_watch_mode=0
_test_mode=0

while [ $# -gt 0 ]; do
    case "$1" in
        --help)
            _help_mode=1
            shift
            ;;
        --watch)
            _watch_mode=1
            shift
            ;;
        --test)
            _test_mode=1
            shift
            ;;
        --) # End of all options
            shift
            break
            ;;
        *) # No more options
            break
            ;;
    esac
done

if [ "$_help_mode" -eq 1 ]; then
    show_help
    exit 0
fi

# Display the status of flags
if [ "$_watch_mode" -eq 1 ]; then
    echo "Interactive mode is enabled."
else
    echo "Interactive mode is not enabled."
fi

if [ "$_test_mode" -eq 1 ]; then
    echo "Testing is enabled."
else
    echo "Testing is not enabled."
fi

cd "$1"
_repo_root="$(git rev-parse --show-toplevel || jj root)"
_path="$(pwd | sed "s|^${_repo_root}||")"
echo "Building in REPO${_path} (cwd: $(pwd))"

case "$_path" in

  /engine/baml-runtime/src/control_flow | /engine/baml-runtime/src/control_flow/* )
    command="INSTA_UPDATE=always cargo test -p baml-runtime control_flow::tests"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,ts,json,j2,hb,hbs,toml,baml,rb \
        --watch "${_repo_root}/engine" \
        --ignore 'target' \
        --ignore 'index.d.ts' \
        --ignore 'index.js' \
        --ignore 'dist' \
        --ignore 'tmp' \
        --verbose \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  /engine/language_client_typescript | /engine/language_client_typescript/* )
    command="pnpm build:debug; date"
    command="${command} && BAML_LOG=debug node cli.js generate --from ../../integ-tests/baml_src --to integ-tests-ts-v2"
    command="${command} && BAML_LOG=debug infisical run -- pnpm ts-node test.ts"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,ts,json,j2,hb,hbs,toml,baml,rb \
        --watch "${_repo_root}/engine" \
        --ignore 'target' \
        --ignore 'index.d.ts' \
        --ignore 'index.js' \
        --ignore 'dist' \
        --ignore 'tmp' \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  /engine/language_client_ruby | /engine/language_client_ruby/* )
    command="RB_SYS_CARGO_PROFILE=dev2 rake compile; date"

    if [ "$_test_mode" -eq 1 ]; then
      #command="wasm-pack test --chrome . --features=wasm"
      #command="wasm-pack test --node . --features=wasm"
      command="${command} && rake test"
    fi

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,j2,toml,baml,rb,gemspec,Gemfile \
        --watch "${_repo_root}/engine" \
        --ignore 'target' \
        --ignore 'tmp' \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  /engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox | /engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/* )
    command="cd ${_repo_root}/engine && UPDATE=1 cargo test -p baml-lib --test mermaid_graph_tests -- --nocapture"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,toml,baml \
        --watch "${_repo_root}/engine" \
        --ignore '**/target/**' \
        --ignore '**/target-rust-analyzer/**' \
        --ignore '**/tmp/**' \
        --ignore 'prompt_fiddle_example.baml' \
        --ignore 'baml_cli_init.baml' \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;

  /engine/baml-schema-wasm | /engine/baml-schema-wasm/* )
    command="cargo build --target=wasm32-unknown-unknown"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,toml \
        --watch "${_repo_root}/engine" \
        --ignore 'target' \
        --ignore 'target-rust-analyzer' \
        --ignore 'tmp' \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;
  
  /engine/generators/go | /engine/generators/go/* )
    command="cargo test --package generators-go --lib -- tests --show-output"
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,toml,j2 \
        --watch "${_repo_root}/engine" \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  # Subdirectories of engine must be matched before this line
  /engine/baml-runtime | /engine/baml-runtime/* )
    if [ "$_test_mode" -eq 1 ]; then
      #command="wasm-pack test --chrome . --features=wasm"
      #command="wasm-pack test --node . --features=wasm"
      # command="cargo test --features=no_wasm,internal internal --verbose -- --nocapture"
      command="cargo test --features=internal internal -- --nocapture"
    else
      command="cargo build --features=wasm --target=wasm32-unknown-unknown"
      command="cargo build --features=internal"
    fi
    #command="cargo test --features=no_wasm"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,hb,hbs,j2,toml \
        --watch "${_repo_root}/engine" \
        --ignore 'target' \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  # Subdirectories of engine must be matched before this line
  /engine | /engine/* )
    command=""
    if [ "$_test_mode" -eq 1 ]; then
      # command="cargo test ${command}"
      command="cd ${_repo_root}/engine/baml-lib/baml && UPDATE_EXPECT=1 cargo nextest run enum_comparison_edge_cases enum_string_comparison"
    else
      command="cargo build"
    fi

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,hb,hbs,j2,toml \
        --watch "${_repo_root}/engine" \
        --ignore 'target/**' \
        --exec "${command}"
    else
      eval "${command}"
    fi
    ;;

  /integ-tests/baml_src | /integ-tests/baml_src/* )
    command="cd ${_repo_root}/engine"
    command="${command} && git checkout -- ${_repo_root}/integ-tests/baml_src/formatter/test-comments.baml"
    command="${command} && cargo run --bin baml-cli fmt ${_repo_root}/integ-tests/baml_src/formatter/test-comments.baml"
    command="${command} && git difftool --extcmd icdiff -y -- ${_repo_root}/integ-tests/baml_src/formatter/test-comments.baml"
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext py,pyi,rs,j2,toml \
        --watch "${_repo_root}/engine" \
        --watch . \
        --ignore 'baml_client/**' \
        --ignore 'target/**' \
        --ignore .pytest_cache \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;

  /integ-tests/python | /integ-tests/python/* )
    command="true"
    command="uv run maturin develop --uv --manifest-path ${_repo_root}/engine/language_client_python/Cargo.toml"
    command="${command} && uv run baml-cli generate --from ${_repo_root}/integ-tests/baml_src"
    if [ "$_test_mode" -eq 1 ]; then
      #command="${command} && BAML_LOG=trace infisical run --env=test -- uv run python baml_example_tracing.py"
      # command="${command} && BAML_LOG=debug,baml_runtime=trace infisical run --env=dev -- uv run pytest -s"
      # command="${command} && BAML_LOG=debug,baml_runtime=trace infisical run --env=dev -- uv run pytest -s tests/test_functions.py::test_openai_responses_all_roles"
      # command="${command} && BAML_LOG=debug,baml_runtime=trace infisical run --env=dev -- uv run pytest -s tests/test_errors.py"
      # command="${command} && BAML_LOG=debug,baml_runtime=trace  uv run pytest -s tests/test_functions.py::test_aws_bedrock_invalid_endpoint"
      command="${command} && BAML_LOG=info infisical run --env=dev -- uv run pytest -s tests/providers/test_aws_video_request.py"
      # command="${command} && BAML_LOG=info  uv run pytest -s tests/test_dynamic_enum_request.py"
    fi
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext py,pyi,rs,j2,toml,baml \
        --watch "${_repo_root}/engine" \
        --watch . \
        --ignore 'baml_client/**' \
        --ignore 'target/**' \
        --ignore .pytest_cache \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;

  /integ-tests/typescript | /integ-tests/typescript/* )
    #BAML_LOG="baml_runtime::tracing=trace,baml_runtime::types::context_manager=debug,baml_runtime=debug"
    # BAML_LOG="baml_runtime=debug"
    command="(cd ${_repo_root}/engine/language_client_typescript && pnpm build:debug)"
    command="${command} && pnpm baml-cli generate --from ${_repo_root}/integ-tests/baml_src"
    if [ "$_test_mode" -eq 1 ]; then
      # command="${command} && infisical run --env=dev -- pnpm integ-tests tests/type-aliases.test.ts"
      command="${command} && infisical run --env=dev -- pnpm integ-tests tests/providers/aws-video-request.test.ts "
      # command="${command} && infisical run -- pnpm integ-tests tests/modular.test.ts"
      # command="${command} && infisical run -- pnpm integ-tests tests/prompt_renderer.test.ts"
      # command="${command} && infisical run -- pnpm integ-tests tests/providers/openai.test.ts"
      # command="${command} && infisical run -- pnpm integ-tests tests/logger.test.ts"
      # command="${command} && infisical run -- pnpm integ-tests tests/type-aliases.test.ts"
      # command="${command} && infisical run -- pnpm integ-tests tests/abort-handlers.test.ts"
      # command="${command} && infisical run --env=dev -- pnpm integ-tests tests/providers/aws.test.ts"
      # command="${command} && BAML_LOG=${BAML_LOG}  infisical run -- pnpm test tests/integ-tests.test.ts"
      # command="${command} && BAML_LOG=${BAML_LOG} pnpm test tests/dynamic-enum-request.test.ts"
    fi
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --delay 1.5 \
        --ext py,pyi,rs,j2,toml,test.ts \
        --watch "${_repo_root}/engine" \
        --watch . \
        --ignore baml_client/** \
        --ignore dist/** \
        --ignore target/** \
        --ignore node_modules/** \
        --ignore *.d.ts \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;

  /integ-tests/ruby | /integ-tests/ruby/* )
    command="rake generate"
    if [ "$_test_mode" -eq 1 ]; then
      command="${command} && infisical run --env=dev -- rake test"
    fi
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext bundle,rb \
        --watch "${_repo_root}/engine/language_client_ruby/lib/" \
        --watch . \
        --ignore baml_client \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;
  
  /integ-tests/go | /integ-tests/go/* )
    # Build the CFFI library that Go depends on
    command="(cd ${_repo_root}/engine/language_client_cffi && cargo make)"

    if [ "$_test_mode" -eq 1 ]; then
      # Set BAML_LIBRARY_PATH and run tests with verbose output
      # The library path must point to the compiled CFFI shared library
      # command="${command} && BAML_LIBRARY_PATH=${_repo_root}/engine/target/debug/libbaml_cffi.dylib infisical run --env=test -- go test -timeout 0 -v -run TestTimeoutBehavior"
      command="${command} && BAML_LIBRARY_PATH=${_repo_root}/engine/target/debug/libbaml_cffi.dylib infisical run --env=test -- bash -lc 'set -o pipefail; go test -timeout 0 -v | grep -v \"^=== RUN   TestCollectorBeforeStreaming$\"'"
    fi

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext rs,toml,j2,go,baml \
        --watch "${_repo_root}/engine" \
        --watch "${_repo_root}/integ-tests/baml_src" \
        --watch "${_repo_root}/integ-tests/go" \
        --ignore "${_repo_root}/integ-tests/go/baml_client" \
        --exec "${command}"
    else
      eval "${command}"
      date
    fi
    ;;

  /typescript/apps/fiddle-web-app | /typescript/apps/fiddle-web-app/* )
    command="(cd ${_repo_root} && turbo run dev --filter=@baml/fiddle-web-app... --force)"

    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext grammar,js,ts,tsx,json \
        --ignore '**/dist' \
        --exec "${command}"
    else
      eval "${comamnd}"
      date
    fi
    ;;
    

  /typescript/packages/codemirror-lang-baml | /typescript/packages/codemirror-lang-baml/* )
    if [ "$_watch_mode" -eq 1 ]; then
      npx nodemon \
        --ext grammar,js,ts,tsx,json \
        --ignore '**/dist' \
        --exec 'pnpm prepare; date'
    else
      pnpm prepare
      date
    fi
    ;;

  /typescript | /typescript/* )
    # This also consume schanges to baml-schema-wasm
    if [ "$_watch_mode" -eq 1 ]; then
      # nodemon config in typescript/nodemon.json
      npx nodemon \
        --ext js,ts,tsx,rs,hb,hbs,toml,html \
        --watch "${_repo_root}/engine" \
        --watch "${_repo_root}/engine/jinja" \
        --watch "${_repo_root}/typescript" \
        --ignore '.next/**' \
        --ignore 'dist/**' \
        --ignore 'node_modules/**' \
        --ignore 'out/**' \
        --ignore 'baml-schema-wasm/web/**' \
        --ignore 'baml-schema-wasm/nodejs/**' \
        --exec 'npx turbo build --force; date'
    else
      npx turbo build --force
      date
    fi
    ;;

  *)
    echo "Nothing to build in repo root"
    ;;

esac
